package cn.syncrond.tcm.util;

import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;

/**
 * @Description: Crontab类型的字符串定义
 * @author meepai
 * @date 2016-11-21
 */
public class CrontabParser {
	/* 支持数字,空格,*,/,逗号,空格的6种元素的组合方式 */
	public static final String COMBINE_PATTERN = "^[\\d\\s\\*-\\/,]+$";
	/* 每个元素的设定方式 */
	public static final String ELE_PATTERN = "^(\\d+(-\\d+(\\/\\d+)?)?(\\,\\d+(-\\d+(\\/\\d+)?)?)*)|(\\*(\\/\\d+)?)$";
	
	/**
	 * @Title: validateTimerText
	 * @Description: 验证定时时间文本设置的有效性
	 * @param @param text
	 * @param @return   
	 * @return int    返回类型    0-验证通过   1-字段为空   2-不符合基本组合方式    3-字段空格分隔的长度不够   4-字段元素不符合格式
	 * 						5-分钟设置数据超限   6-小时设置数据超限   7-日设置数据超限   8-月设置数据超限   9-星期设置数据超限
	 * 						10-分钟数据上下限设置异常   11-小时数据上下限设置异常  12-日数据上下限设置异常  13-月数据上下限设置异常  14-星期数据上下限设置异常
	 * @throws
	 */ 
	public static int validateTimerText(String text){
		if(StringUtils.isBlank(text)){
			return 1;
		}
		/* 判断是否满足基本的组合方式 */
		if(!text.matches(COMBINE_PATTERN)){
			return 2;
		}
		String[] strs = text.split("\\s+");
		/* 基本的六部分必须满足  不设定可以使用*代替 */
		if(strs.length < 5){
			return 3;
		}
		String minute;
		String hour;
		String day;
		String month;
		String week;
		/* 校验前5个字段是否符合规则 */
		for(int i=0; i<5; i++){
			if(!strs[i].matches(ELE_PATTERN)){
				return 4;
			}
			switch(i){
			case 0:
				minute = strs[0];
				if(!validateRange(minute, 0, 59)){
					return 5;
				}
				if(!validateRangeValueSet(minute)){
					return 10;
				}
				break;
			case 1:
				hour = strs[1];
				if(!validateRange(hour, 0, 23)){
					return 6;
				}
				if(!validateRangeValueSet(hour)){
					return 11;
				}
				break;
			case 2:
				day = strs[2];
				if(!validateRange(day, 1, 31)){
					return 7;
				}
				if(!validateRangeValueSet(day)){
					return 12;
				}
				break;
			case 3:
				month = strs[3];
				if(!validateRange(month, 1, 12)){
					return 8;
				}
				if(!validateRangeValueSet(month)){
					return 13;
				}
				break;
			case 4:
				week = strs[4];
				if(!validateRange(week, 0, 6)){
					return 9;
				}
				if(!validateRangeValueSet(week)){
					return 14;
				}
				break;
			}
		}
		return 0;
	}
	
	/**
	 * @Title: getValidateText
	 * @Description: 获取验证返回信息
	 * @param @param text
	 * @param @return   
	 * @return String    返回类型
	 * @throws
	 */
	public static String getValidateText(String text){
		int ret = validateTimerText(text);
		switch(ret){
		case 0:
			return "验证通过";
		case 1:
			return "传入字段为空";
		case 2:
			return "不符合基本的数字,空格,*,/,逗号的5种元素组合方式";
		case 3:
			return "字段空格分隔的长度不够";
		case 4:
			return "字段元素不符合格式规范";
		case 5:
			return "分钟设置数据超限";
		case 6:
			return "小时设置数据超限";
		case 7:
			return "日设置数据超限";
		case 8:
			return "月设置数据超限";
		case 9:
			return "星期设置数据超限";
		case 10:
			return "分钟数据上下限设置异常";
		case 11:
			return "小时数据上下限设置异常";
		case 12:
			return "日数据上下限设置异常";
		case 13:
			return "月数据上下限设置异常";
		case 14:
			return "星期数据上下限设置异常";
		}
		return null;
	}
	
	
	/**
	 * @Title: getTimerElement
	 * @Description: 
	 * @param text
	 * @return   
	 * @return Map<String,String>    返回类型
	 * @throws
	 */ 
	public static Map<String, String> parseTimerParams(String text){
		Map<String, String> map = null;
		if(validateTimerText(text) == 0){
			map = new HashMap<String, String>();
			String[] strs = text.split("\\s+");
			map.put("minute", strs[0]);
			map.put("hour", strs[1]);
			map.put("day", strs[2]);
			map.put("month", strs[3]);
			map.put("week", strs[4]);
		}
		return map;
	}
	
	/**
	 * @Title: judge
	 * @Description: 判断一个时间是否在给定时间文本的表达范围内 
	 * @param text
	 * @param date
	 * @return boolean    返回类型
	 * @throws
	 */ 
	public static boolean judge(String text, Date date){
		Map<String, String> map = parseTimerParams(text);
		return judgeFromMap(map, date);
	}
	
	/**
	 * @Title: judgeFromMap
	 * @Description: 从map中抽取定时信息，对比时间
	 * @param map
	 * @param date
	 * @return   
	 * @return boolean    返回类型
	 * @throws
	 */ 
	public static boolean judgeFromMap(Map<String, String> map, Date date){
		if(map != null){
			Calendar c = Calendar.getInstance();
			c.setTime(date);
			String minute = map.get("minute");
			String hour = map.get("hour");
			String day = map.get("day");
			String month = map.get("month");
			String week = map.get("week");
			
			/* Calendar得到的月份区间为[0,11]，得到的星期区间为[1,7]，其中星期天为1 */
			boolean minuteIn = validateIn(minute, c.get(Calendar.MINUTE), 0, 59);
			boolean hourIn = validateIn(hour, c.get(Calendar.HOUR_OF_DAY), 0, 23);
			boolean dayIn = validateIn(day, c.get(Calendar.DAY_OF_MONTH), 1, 31);
			boolean monthIn = validateIn(month, c.get(Calendar.MONTH)+1, 1, 12);
			boolean weekIn = validateIn(week, c.get(Calendar.DAY_OF_WEEK)-1, 0, 6);
			boolean combine = false;
			if(day.equals("*") && week.equals("*")){
				combine = true;
			} else if(day.equals("*")){
				combine = weekIn;
			} else if(week.equals("*")){
				combine = dayIn;
			} else {
				combine = dayIn || weekIn;
			}
			if(minuteIn && hourIn && monthIn && combine){
				return true;
			}
		}
		return false;
	}
	
	/**
	 * @Title: validate
	 * @Description: 验证某个元素的数值设置情况
	 * @param @param str
	 * @param @param start
	 * @param @param end
	 * @param @return   
	 * @return boolean    返回类型
	 * @throws
	 */ 
	private static boolean validateRange(String str, int start, int end){
		Pattern ptn = Pattern.compile("\\d+");
		Matcher matcher = ptn.matcher(str);
		while(matcher.find()){
			String group = matcher.group();
			int num = Integer.parseInt(group);
			if(num < start || num > end){
				return false;
			}
		}
		return true;
	}
	
	/**
	 * @Title: validateRangeValueSet
	 * @Description: 验证上下限设置的有效性 
	 * @param str
	 * @return   
	 * @return boolean    返回类型
	 * @throws
	 */ 
	private static boolean validateRangeValueSet(String str){
		Pattern ptn = Pattern.compile("\\d+\\-\\d+");
		Matcher matcher = ptn.matcher(str);
		while(matcher.find()){
			String group = matcher.group();
			String[] ss = group.split("\\-");
			if(Integer.parseInt(ss[1]) <= Integer.parseInt(ss[0])){
				return false;
			}
		}
		return true;
	}
	
	/**
	 * @Title: validateIn
	 * @Description: 验证一个数字在字符串所表示的范围内 
	 * @param str
	 * @param num
	 * @return boolean    返回类型
	 * @throws
	 */ 
	private static boolean validateIn(String str, int num, int start, int end){
		/* 对区间的判断 */
		if(num < start || num > end){
			return false;
		}
		/* 对*号的解析 */
		if(str.equals("*")){
			return true;
		}
		/* 对*\\/数字的解析 */
		if(str.matches("\\*\\/\\d+")){
			int d = Integer.parseInt(str.substring(str.indexOf("/") +1));
			if((num - start) % d != 0){
				return false;
			} else {
				return true;
			}
		}
		String[] strs = str.split(",");
		Pattern ptn;
		Matcher matcher;
		for(int i=0; i<strs.length; i++){
			if(strs[i].matches("\\d+")){
				int k = Integer.parseInt(strs[i]);
				if(k == num){
					return true;
				}
			}
			ptn = Pattern.compile("\\d+\\-\\d+");
			matcher = ptn.matcher(strs[i]);
			int upper = -1;		/* 为了校验方便设置了必下限小的值 */
			int lower = 0;
			int per = 1;
			if(matcher.find()){
				String group = matcher.group();
				String[] ss = group.split("-");
				upper = Integer.parseInt(ss[1]);
				lower = Integer.parseInt(ss[0]);
			} 
			ptn = Pattern.compile("\\/(\\d+)");
			matcher = ptn.matcher(strs[i]);
			if(matcher.find()){
				per = Integer.parseInt(matcher.group(1));
			}
			if(num <= upper && num >= lower){
				if((num-lower) % per == 0){
					return true;
				}
			}
		}
		return false;
	}
	
	public static void main(String[] args){
		String str = "24-30/10,20-28/1 8-23/7 3-3 11 5";
		Date date = new Date();
		Calendar c = Calendar.getInstance();
		c.setTime(date);
//		System.out.println(100%1);
//		System.out.println(TimerParser.getValidateText(str));
		System.out.println(CrontabParser.judge(str, date));
	}
}
